/*--------------------------------------------------------------------------
 DEPUI-GFX-TK 3.0 - GPL portable source code libraries 
 http://www.deleveld.dds.nl/depui.htm
 See file docs/copying for copyright details
 ---------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include "detk/detk.h"
#include "deds/deds.h"

/*
MODULE: VECTOR - An almost typesafe C vector
-----------------------------------------------
This module is an almost typesafe C vector class.  You use it by defining a
structure of what type of elements you want to have in the vector.  For example
lets make a vector of doubles.  You use the vector module by defining a type
to carry your data.  Only the mx_vector_insert function is not typesafe.

REQUIRES: nothing
*/

#define MXMODULE_VECTOR

/* Make a vector of the type that you want */
typedef MX_VECTOR(double) DOUBLEVECTOR;

static void print_vector(DOUBLEVECTOR * vector)
{
	unsigned i;

	fprintf(stderr, "Vector has size %i\n", (int)mx_vector_size(vector));

	for (i = 0; i < mx_vector_size(vector); i++) {
		const double element = vector->data[i];

		fprintf(stderr, "%f ", element);
	}
	fprintf(stderr, "\n");
}

static void test_vector(void)
{
	double firstpart[5] = { 0., 1., 2., 3., 4. };
	double middle = 5.;
	double lastpart[5] = { 6., 7., 8., 9., 10. };
	DOUBLEVECTOR myvector;

	fprintf(stderr, "\n\nTesting module VECTOR\n");

	/* Construct the vector */
	mx_vector(&myvector);

	/* Add some data */
	mx_vector_append(&myvector, firstpart, 5);
	print_vector(&myvector);

	mx_vector_append(&myvector, &middle, 1);
	print_vector(&myvector);

	mx_vector_append(&myvector, lastpart, 5);
	print_vector(&myvector);

	/* Remove some data and put it back */
	mx_vector_remove(&myvector, 2, 1);
	print_vector(&myvector);

	mx_vector_insert(&myvector, &firstpart[2], 1, 2);
	print_vector(&myvector);

	/* Destroy the vector */
	mx_vector_free(&myvector);
}

/*
MODULE: UTF8 - Some simple UTF8 decoding functions
------------------------------------------
This module is some *very* simple utf8 functions.

REQUIRES: nothing
*/

#define MXMODULE_UTF8

static void test_utf8(void)
{
	int clen;
	const char *message = "This is a nice string.";
	const char *ptr = message;

	fprintf(stderr, "\n\nTesting module UTF8\n");

	/* Start of the string */
	clen = mx_utf8_len(message);
	while (clen) {
		const long c = mx_utf8_char(ptr, clen);

		fprintf(stderr, "\'%c\' = %i\n", (char) c, (int) c);

		/* Get the next character */
		ptr += clen;
		clen = mx_utf8_len(ptr);
	}
}

/*
MODULE: REGION - Rectangular region clipping function
-----------------------------------------------------
This module handles clipping in rectangular regions.

REQUIRES: VECTOR
*/

#define MXMODULE_REGION

static void test_region(void)
{
	unsigned i;
	MX_REGION region;
	MX_RECT whole = { 0, 0, 640, 480 };
	MX_RECT part1 = { 100, 100, 300, 300 };
	MX_RECT part2 = { 200, 200, 400, 400 };

	fprintf(stderr, "\n\nTesting module REGION\n");

	/* Start with the whole area */
	mx_vector(&region);
	mx_vector_append(&region, &whole, 1);

	/* Remove some parts */
	mx_region_clip(&region, &part1);
	mx_region_clip(&region, &part2);

	for (i = 0; i < mx_vector_size(&region); i++) {
		const MX_RECT *rect = &region.data[i];

		fprintf(stderr, "{ %5i, %5i, %5i, %5i }\n", rect->x1, rect->y1,
				rect->x2, rect->y2);
	}
	fprintf(stderr, "\n");
}

/*
MODULE: ATOM - Reference counting and delete locking
-----------------------------------------------------
This module adds reference counting and delete locking to another data struct.

REQUIRES: nothing
*/

#define MXMODULE_ATOM

/* A string type derived from atom */
typedef struct STRINGATOM {
	union {
		MX_ATOM atom;
	} base;

	const char *string;
} STRINGATOM;

/* Destructor */
static void stringatom_destruct(void *atom)
{
	STRINGATOM *theatom = (STRINGATOM *) atom;

	fprintf(stderr, "-> destruct %s\n", theatom->string);
}

/* Constructor */
STRINGATOM *stringatom(STRINGATOM * theatom, const char *string)
{
	theatom =
		(STRINGATOM *) mx_atom(theatom, stringatom_destruct,
							   sizeof(STRINGATOM));
	if (theatom) {
		theatom->string = string;
		fprintf(stderr, "-> construct %s\n", theatom->string);
	}
	return theatom;
}

static void test_atom(void)
{
	MX_LOCK lock1;
	MX_LOCK lock2;
	STRINGATOM atom;

	fprintf(stderr, "\n\nTesting module ATOM\n");

	/* Two different ways to construct an stringatom */
	stringatom(&atom, "String one");

	fprintf(stderr, "Locked atoms cant be deleted\n");
	mx_lock(&lock1, &atom);
	mx_lock(&lock2, &atom);
	mx_delete(&atom);

	fprintf(stderr, "Even if you try to delete more than once\n");
	mx_delete(&atom);
	mx_delete(&atom);
	mx_delete(&atom);

	fprintf(stderr, "There is still a lock present\n");
	mx_unlock(&lock1);
	mx_delete(&atom);
	mx_delete(&atom);

	fprintf(stderr, "Atoms get deleted only when the last lock is removed\n");
	mx_unlock(&lock2);

	fprintf(stderr, "Atom is deleted in last unlock\n");
}

/*
MODULE: DRS - Dirty rectangle system
-----------------------------------------------------
This module handles a dirty rectangle system.

REQUIRES: REGION (VECTOR)
*/

#define MXMODULE_DRS

static void flush(MX_RECT * rect)
{
	fprintf(stderr, "{ %5i, %5i, %5i, %5i }\n", rect->x1, rect->y1, rect->x2,
			rect->y2);
}

static void test_drs(void)
{
	MX_RECT part1 = { 100, 100, 300, 300 };
	MX_RECT part2 = { 200, 200, 400, 400 };

	fprintf(stderr, "\n\nTesting module DRS\n");

	/* Set the entire area */
	mx_drs_area(639, 479);
	mx_drs_update(0);

	/* Mark some areas for updating */
	mx_drs_dirty(&part1, true);
	mx_drs_dirty(&part2, true);

	/* Tell the dirty parts */
	mx_drs_update(flush);
}

/*
TEST ALL THE MODULES
--------------------
*/

/* Bring in all the modules */
#include "detk/detk.c"
#include "deds/deds.c"

int main(void)
{
	test_vector();
	test_utf8();
	test_region();
	test_atom();
	test_drs();
	return 0;
}
